﻿package com.firstpixel.physics
{
	import flash.display.Sprite;

	import flash.geom.Point;
	import Box2D.Dynamics.*;
	import Box2D.Dynamics.Joints.*;
	import Box2D.Dynamics.Contacts.*;
	import Box2D.Collision.Shapes.*;
	import Box2D.Common.Math.*;
	import Box2D.Common.*;	
	import Box2D.Collision.*;

	import flash.display.MovieClip;
	
	public class Box2DGenerator  
	{
		private var _world:b2World;
		private var _scale:Number = 1;
		private var _graphicalTarget:MovieClip;

		public function Box2DGenerator(world:b2World, scale:Number, graphicalTarget:MovieClip = null)
		{
			_world = world;
			_scale = scale;
			_graphicalTarget = graphicalTarget;
		}
				
		public function newBox(x:Number, y:Number, w:Number, h:Number, r:Number=0,bullet:Boolean=false):b2Body
		{
			var bd:b2BodyDef;
			var box:b2PolygonDef = new b2PolygonDef();
			var body:b2Body;
			
			box.SetAsBox(w / _scale, h / _scale);
			if(bullet){
				box.density = 1.0;
			}else{
				box.density = 0;
			}
			box.friction = 0.5;
			box.restitution = 0.5;
			bd = new b2BodyDef();
			bd.isBullet = bullet
			bd.angularDamping = 10;
						
			bd.position.Set(x / _scale, y / _scale);
			bd.angle = r; 
			trace("ANGLE :"+r)
			body = _world.CreateBody(bd);
			body.CreateShape(box);
			body.SetMassFromShapes();
			return body;
		}	
		
		public function newCircle(x:Number, y:Number, r:Number):b2Body
		{
			var bd:b2BodyDef;
			var body:b2Body;
			var circ:b2CircleDef = new b2CircleDef();
			circ.radius = r / _scale;
			circ.density = 1.0;
			circ.friction = 0.4;
			circ.restitution = 0.5;
			bd = new b2BodyDef();
			bd.position.Set(x / _scale, y / _scale);
			body = _world.CreateBody(bd);
			body.CreateShape(circ);
			body.SetMassFromShapes();			
			return body;
		}
			
		public function newPin(body1, body2, v:b2Vec2):b2RevoluteJoint
		{
			var jointDef:b2RevoluteJointDef = new b2RevoluteJointDef();
			jointDef.Initialize(body1, body2, new b2Vec2(v.x / _scale, v.y / _scale ) );//body1.GetWorldCenter() );//new b2Vec2(x/ _scale, y/ _scale) );
			return _world.CreateJoint(jointDef) as b2RevoluteJoint;			
		}		
		
		public function newPearlNecklace(x:Number, y:Number, w:Number, h:Number,  pearlCount:uint):void
		{
			var r:Number = 10;
			var i:int = 0;
			var body:b2Body;
			var lastBody:b2Body;
			var xStep:Number = w/pearlCount;
			var yStep:Number = h/pearlCount;
			for(i=0; i<pearlCount; i++){
				body = newCircle(x+xStep*i, y+yStep*i, r);
				if(lastBody){					
					var p:b2RevoluteJoint = newPin(body, lastBody, lastBody.GetWorldCenter() );
					p.EnableLimit(true);
					p.SetLimits(-10 / (180/Math.PI),1 / (180/Math.PI) );					
				}				
				lastBody = body;
			}
		}		
		
		public function joinAtoB(body1, body2):void
		{
			newPin(body1, body2, body1.GetWorldCenter());
		}
		
		//NOTE: the clip MUST be present on the display list for the shape based hitTest to work
		public function convertClipToBox2D(target:MovieClip):Array
		{
			trace("TARGET :"+target)
			var i:int;
			var child:MovieClip;
			var newBody:b2Body;
			var bodies:Array = [];
			var pins:Array = [];
			
			for(i=0;i<target.numChildren; i++){
				trace("TARGET :"+target.getChildAt(i).name)
			
				child = target.getChildAt(i) as MovieClip;
				var tempRotation:Number = child.rotation/180*Math.PI;
				var bullet;
				trace("Child :"+child)
			
				switch(String(child.name).substr(0,6)){
					case 'dynBox':
							bullet = true;
							child.rotation = 0;
							newBody = newBox(child.x , child.y , child.width * 0.5, child.height * 0.5, tempRotation, bullet);
							newBody.SetUserData(child);
							child.correspondingBody = newBody;
							bodies.push( { body : newBody, clip: child  , type:String(child.name).substr(0,6)}  );							
							child.rotation = tempRotation*180/Math.PI;
						break;
					case 'dynBal':				
							newBody = newCircle(child.x, child.y , child.width * 0.5);					
							child.rotation = tempRotation;
							newBody.SetUserData(child);
							child.correspondingBody = newBody;
							bodies.push( { body : newBody, clip: child , type:String(child.name).substr(0,6)}  );							
						break;
					//wall object	
					case 'dynWal':
							child.rotation = 0;
							newBody = newBox(child.x , child.y , child.width * 0.5, child.height * 0.5, tempRotation);
							newBody.SetUserData(child);
							child.correspondingBody = newBody;
							bodies.push( { body : newBody, clip: child  , type:String(child.name).substr(0,6)}  );							
							child.rotation = tempRotation*180/Math.PI;
						break;	
					//cross object	
					case 'dynCro':
							newBody = newCircle(child.x, child.y , child.width * 0.5);					
							child.rotation = tempRotation;
							newBody.SetUserData(child);
							child.correspondingBody = newBody;
							bodies.push( { body : newBody, clip: child  , type:String(child.name).substr(0,6)}  );							
						break;							
					//box object	
					case 'staBox':
					//case 'car':
							bullet = false;
							if( child.name == "car" ) bullet = true;
							child.rotation = 0;
							newBody = newBox(child.x , child.y , child.width * 0.5, child.height * 0.5, tempRotation, bullet);
							newBody.SetUserData(child);
							child.correspondingBody = newBody;
							bodies.push( { body : newBody, clip: child , type:String(child.name).substr(0,6) }  );							
						break;							
					//ball object	
					case 'staBal':				
							newBody = newCircle(child.x, child.y , child.width * 0.5);					
							newBody.SetMass( new b2MassData() );
							child.rotation = tempRotation;
							newBody.SetUserData(child);
							child.correspondingBody = newBody;
							bodies.push( { body : newBody, clip: child , type:String(child.name).substr(0,6) }  );							
						break;
					
					case 'pin':
							var objectsUnderPin:Array = getObjectsUnderPin(child, target);				
							var l:int = objectsUnderPin.length;
							child.visible = false;							
							if(objectsUnderPin.length > 1){
								//remember the pin and body clips. Only the top two, if the pin is over more than 2. 
								pins.push( { pin : child, clip1 : objectsUnderPin[l-1], clip2 : objectsUnderPin[l-2] } );
							}							
						break;										
					//wall object	
					case 'staWal':
							child.rotation = 0;
							newBody = newBox(child.x , child.y , child.width * 0.5, child.height * 0.5, tempRotation);
							newBody.SetMass( new b2MassData() );
							newBody.SetUserData(child);
							child.correspondingBody = newBody;
							bodies.push( { body : newBody, clip: child  , type:String(child.name).substr(0,6)}  );
							child.rotation = tempRotation*180/Math.PI;
						break;	
					//cross object	
					case 'staCro':
							newBody = newCircle(child.x, child.y , child.width * 0.5);					
							newBody.SetMass( new b2MassData() );
							child.rotation = tempRotation;
							newBody.SetUserData(child);
							child.correspondingBody = newBody;
							bodies.push( { body : newBody, clip: child , type:String(child.name).substr(0,6) }  );
							child.rotation = tempRotation*180/Math.PI;
						break;							
				}			
			
			}			
			
			//create pins
			for(i=0;i<pins.length; i++){
				
				var pinBody1:b2Body = pins[i].clip1.correspondingBody;
				var pinBody2:b2Body = pins[i].clip2.correspondingBody;
		
				newPin(pinBody1, pinBody2, new b2Vec2( pins[i].pin.x, pins[i].pin.y ) );
			}
			
			return bodies;
		}	
		
		private function getObjectsUnderPin(pin:MovieClip, target:MovieClip):Array
		{
			var i:int;
			var child:MovieClip;
			var objectsUnderPin:Array = [];
			
			var mc:MovieClip = new MovieClip();
			for(i=0;i<target.numChildren; i++){
				
				var p:Point = target.localToGlobal( new Point( pin.x, pin.y ) );
				
				child = target.getChildAt(i) as MovieClip;
				
				if(child.name != 'pin'){
				
					var hit:Boolean = child.hitTestPoint(p.x, p.y, true);
					
					if( hit ){
						objectsUnderPin.push( child );
					}
				}
			}			
			
			return objectsUnderPin;
		}
		
		
	}
}